{"componentChunkName":"component---src-templates-post-js","path":"/simply-learn-full-stack-2","result":{"data":{"site":{"siteMetadata":{"title":"neohed","description":"Blog posts on web development and related areas","author":{"name":"neohed"},"keywords":["Web Development","JavaScript"]}},"mdx":{"frontmatter":{"title":"Simply Learn Full-Stack Web, Part 2","description":"Tutorial to learn full-stack with React node.js and Prisma DB","date":"June 27, 2022","author":null,"banner":null,"slug":"simply-learn-full-stack-2","keywords":null},"body":"function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"slug\": \"simply-learn-full-stack-2\",\n  \"date\": \"2022-06-27T08:36:32\",\n  \"title\": \"Simply Learn Full-Stack Web, Part 2\",\n  \"description\": \"Tutorial to learn full-stack with React node.js and Prisma DB\",\n  \"published\": true\n};\n\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\n\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, [\"components\"]);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h1\", null, \"Simply Learn Full-Stack React & Node.js\"), mdx(\"h2\", null, \"Add a form to the React client site\"), mdx(\"p\", null, \"We're going to add a few components here to generate our form from our data.  There are much better libraries to do this, which we will look at later, but for now we will write it ourselves.\"), mdx(\"p\", null, \"Create all the following files under the \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"src\"), \" folder in our React project!\"), mdx(\"p\", null, \"Create \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Input.js\"), \" and paste in this code:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-jsx\"\n  }), \"import React, { useEffect, useRef } from \\\"react\\\";\\n\\nconst Input = ({\\nid, value = \\\"\\\", type = \\\"text\\\", readOnly = false, required = false\\n}) => {\\n  const input = useRef(null);\\n\\n  useEffect(() => {\\n    if (input.current) {\\n      const sValue = value.toString();\\n\\n      if (type === 'checkbox') {\\n        input.current.checked = sValue === 'true';\\n        input.current.value = 'true'\\n      } else {\\n        input.current.value = sValue\\n      }\\n    }\\n  }, [type, value])\\n\\n  return (\\n    <input\\n      ref={input}\\n      id={id}\\n      name={id}\\n      type={type}\\n      readOnly={readOnly}\\n      disabled={readOnly}\\n      required={required}\\n    />\\n  );\\n};\\n\\nexport default Input;\\n\")), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"With \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"useEffect\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"useRef\"), \" hooks you can be certain that your uncontrolled inputs will update when the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"value\"), \" prop changes.\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Input.js\"), \" creates text inputs or checkboxes depending on the data type of the \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"value\"), \" parameter. Next we need a component to render a label with an \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Input.js\"), \".\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"React has two kinds of inputs: controlled and uncontrolled. With controlled the value is managed by React in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"useState\"), \". With uncontrolled the value is managed by the DOM. The difference is what the \\\"single source of truth\\\" is. Controlled are ideal when you have a small number of inputs. Uncontrolled perform better and, when your controls are inside a form, it's easier to have a single form event handler rather than an event handler on each input.\")), mdx(\"p\", null, \"Create \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"InputLabel.js\"), \", like this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-jsx\"\n  }), \"import React from \\\"react\\\";\\nimport Input from \\\"./Input\\\";\\n\\nconst InputLabel = ({label, error, info, ...inputProps}) => {\\n    return (\\n        <p\\n            className=\\\"input-label\\\"\\n        >\\n            <label htmlFor={inputProps.id}>\\n                {\\n                    label\\n                }\\n            </label>\\n            <Input\\n                {...inputProps}\\n            />\\n        </p>\\n    );\\n};\\n\\nexport default InputLabel;\\n\")), mdx(\"p\", null, \"And now we make a form component with some string utility functions to turn an object into a bunch of form fields using our \\\"Input\\\" components.\"), mdx(\"p\", null, \"Create \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Form.js\"), \":\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-jsx\"\n  }), \"import React from 'react';\\nimport InputLabel from \\\"./InputLabel\\\";\\nimport './form.css'\\n\\nconst isNullOrUndefined = prop => prop === null\\n    || prop === undefined;\\nconst isEmptyString = prop => isNullOrUndefined(prop)\\n    || prop === '';\\nconst capitalize = word =>\\n    word.charAt(0).toUpperCase() +\\n    word.slice(1).toLowerCase();\\n\\nfunction titleFromName(name) {\\n    if (isEmptyString(name)) {\\n        return '';\\n    }\\n\\n    return name.split(/(?=[A-Z])|\\\\s/).map(s => capitalize(s)).join(' ')\\n}\\n\\nconst Form = ({entity}) => {\\n  return (\\n    <form>\\n      {\\n        Object.entries(entity).map(([entityKey, entityValue]) => {\\n          if (entityKey === \\\"id\\\") {\\n            return <input\\n              type=\\\"hidden\\\"\\n              name=\\\"id\\\"\\n              key=\\\"id\\\"\\n              value={entityValue}\\n            />\\n          } else {\\n            return <InputLabel\\n              id={entityKey}\\n              key={entityKey}\\n              label={titleFromName(entityKey)}\\n              type={\\n                typeof entityValue === \\\"boolean\\\"\\n                  ? \\\"checkbox\\\"\\n                  : \\\"text\\\"\\n                }\\n                value={entityValue}\\n              />\\n            }\\n          })\\n        }\\n      <button\\n        type=\\\"submit\\\"\\n      >\\n        Submit\\n      </button>\\n    </form>\\n  );\\n};\\n\\nexport default Form;\\n\\n\")), mdx(\"p\", null, \"And create \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"form.css\"), \":\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-css\"\n  }), \"form {\\n    padding: 1em;\\n    background: #f9f9f9;\\n    border: 1px solid #c1c1c1;\\n    margin: 2rem auto 0 auto;\\n    max-width: 600px;\\n}\\n\\nform button[type=submit] {\\n    margin-left: 159px;\\n}\\n\\n.input-label {\\n    display: flex;\\n}\\n\\n.input-label label {\\n    font-weight: bold;\\n}\\n\\n.input-label input {\\n    margin-left: 12px;\\n}\\n\\n@media (min-width: 400px) {\\n    label {\\n        text-align: right;\\n        flex: 1;\\n    }\\n\\n    input,\\n    button {\\n        flex: 3;\\n    }\\n}\\n\")), mdx(\"p\", null, \"Now change \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"AddEditNote.js\"), \" to use your \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Form.js\"), \" component:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-jsx\"\n  }), \"import React from 'react';\\nimport Form from './Form';\\n\\nconst noteEntity = {\\n    id: 1,\\n    title: 'A Note',\\n    content: 'Lorem ipsum dolor sit amet',\\n    author: 'neohed',\\n    lang: 'en',\\n    isLive: true,\\n    category: '',\\n}\\n\\nconst AddEditNote = () => {\\n    return (\\n        <div>\\n            <Form\\n                entity={noteEntity}\\n            />\\n        </div>\\n    );\\n};\\n\\nexport default AddEditNote;\\n\")), mdx(\"p\", null, \"To test this, inside the \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"node-react-stack/react-client\"), \" folder, run:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-shell\"\n  }), \"npm run start\\n\")), mdx(\"p\", null, \"You should see an HTML form with the values from the noteEntity object.\"), mdx(\"p\", null, \"Now, to make it easier to see what data our app is using we will make a \\\"debug\\\" component. Create a new file, \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"RenderData.js\"), \", like this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-jsx\"\n  }), \"import React from 'react';\\nimport './render-data.css'\\n\\nconst RenderData = ({data}) => {\\n    return (\\n        <div\\n            className='render-data'\\n        >\\n          <pre>\\n            {\\n                JSON.stringify(data, null, 3)\\n            }\\n          </pre>\\n        </div>\\n    );\\n};\\n\\nexport default RenderData;\\n\")), mdx(\"p\", null, \"Create \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"render-data.css\"), \":\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-css\"\n  }), \"@import url('https://fonts.googleapis.com/css2?family=Fira+Code&display=swap');\\n\\n.render-data > pre {\\n    font-family: 'Fira Code', monospace;\\n    font-size: 1.2em;\\n    padding: 8px 0 0 32px;\\n}\\n\")), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Fira Code\"), \" is a nice monospace font provided by google. Monospace fonts are ideal for displaying code or data.\")), mdx(\"p\", null, \"And finally, edit \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"AddEditNote.js\"), \", like this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-jsx\"\n  }), \"import React from 'react';\\nimport RenderData from \\\"./RenderData\\\";\\nimport Form from './Form';\\n\\nconst noteEntity = {\\n    id: 1,\\n    title: 'A Note',\\n    content: 'Lorem ipsum dolor sit amet',\\n    author: 'neohed',\\n    lang: 'en',\\n    isLive: true,\\n    category: '',\\n}\\n\\nconst AddEditNote = () => {\\n    return (\\n        <div>\\n            <RenderData\\n                data={noteEntity}\\n            />\\n            <Form\\n                entity={noteEntity}\\n            />\\n        </div>\\n    );\\n};\\n\\nexport default AddEditNote;\\n\")), mdx(\"p\", null, \"If you run the React app now, you should see a screen like this:\"), mdx(\"p\", null, mdx(\"span\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"933px\"\n    }\n  }), \"\\n      \", mdx(\"a\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"gatsby-resp-image-link\",\n    \"href\": \"/static/1402b9a05c25cf4a16d550066c5a98c2/ac746/screen-shot.jpg\",\n    \"style\": {\n      \"display\": \"block\"\n    },\n    \"target\": \"_blank\",\n    \"rel\": [\"noopener\"]\n  }), \"\\n    \", mdx(\"span\", _extends({\n    parentName: \"a\"\n  }, {\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"67.95366795366795%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAQACBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe8gaiP/xAAVEAEBAAAAAAAAAAAAAAAAAAAQAf/aAAgBAQABBQJr/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRABAAIDAAAAAAAAAAAAAAAAAQAQESFB/9oACAEBAAE/ITPW9iFf/9oADAMBAAIAAwAAABAjD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABgQAQEBAQEAAAAAAAAAAAAAAAEAEVFB/9oACAEBAAE/EAIE7tp2GgewMEMsv//Z')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  })), \"\\n  \", mdx(\"img\", _extends({\n    parentName: \"a\"\n  }, {\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"App Screenshot\",\n    \"title\": \"App Screenshot\",\n    \"src\": \"/static/1402b9a05c25cf4a16d550066c5a98c2/ac746/screen-shot.jpg\",\n    \"srcSet\": [\"/static/1402b9a05c25cf4a16d550066c5a98c2/8356d/screen-shot.jpg 259w\", \"/static/1402b9a05c25cf4a16d550066c5a98c2/bc760/screen-shot.jpg 518w\", \"/static/1402b9a05c25cf4a16d550066c5a98c2/ac746/screen-shot.jpg 933w\"],\n    \"sizes\": \"(max-width: 933px) 100vw, 933px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  })), \"\\n  \"), \"\\n    \")), mdx(\"p\", null, \"You could just \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"console.log\"), \" the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"noteEntity\"), \" object, but sometimes it's easier to understand things when you use a component like this to render the object in the browser window.\"), mdx(\"p\", null, \"Next we will \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"/simply-learn-full-stack-3\"\n  }), \"create the node.js server\"), \"...\"), mdx(\"p\", null, \"Code repo: \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://github.com/neohed/node-react-stack\"\n  }), \"Github Repository\")));\n}\n;\nMDXContent.isMDXComponent = true;"}},"pageContext":{"id":"926132dd-bb88-57a0-bc31-4b9579c53a0d","prev":{"id":"b005ef5d-4ace-5222-b023-d44db83deae2","parent":{"name":"index","sourceInstanceName":"blog"},"excerpt":"Simply Learn Full-Stack React & Node.js Now we're going to  POST  data to our server from the client. Previously we've used HTTP GET requests which are for getting data.  To add data we use HTTP POST. First we need to make a few small changes to our…","fields":{"title":"simply learn-full-stack-6","description":"Full-Stack React & Node.js - HTTP POST","slug":"simply-learn-full-stack-6","absolutePath":"D:/Workspace/Github/neohed-blog/content/blog/learn-full-stack-simply-06/index.mdx","banner":null,"date":"2022-11-21T11:19:35"}},"next":{"id":"b2d39b32-1682-5417-9f36-48b85c6d930b","parent":{"name":"index","sourceInstanceName":"blog"},"excerpt":"Simply Learn Full-Stack React & Node.js An easy tutorial series that teaches full-stack with React, Node.js, Prisma and SqlLite DB This series is simple and fast.  The code repo link will be at the bottom of each post. To use this tutorial there's no…","fields":{"title":"Simply Learn Full-Stack Web","description":"Tutorial to learn full-stack with React node.js and Prisma DB","slug":"simply-learn-full-stack-intro","absolutePath":"D:/Workspace/Github/neohed-blog/content/blog/learn-full-stack-simply-00/index.mdx","banner":null,"date":"2022-06-27T08:36:32"}}}}}